package cn.tedu.charging.cost.service.impl;

import cn.tedu.charging.common.pojo.param.ChargingProcessParam;
import cn.tedu.charging.cost.dao.repository.CostRuleCacheRepository;
import cn.tedu.charging.cost.dao.repository.CostRuleRepository;
import cn.tedu.charging.cost.pojo.data.ChargingData;
import cn.tedu.charging.cost.pojo.po.ChargingDataTime;
import cn.tedu.charging.cost.pojo.po.CostRulePO;
import cn.tedu.charging.common.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.cost.service.CostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class CostServiceImpl implements CostService {

    //调用Redis缓存数据库
    @Autowired
    private CostRuleCacheRepository cacheRepository;

    //调用Maraidb数据库
    @Autowired
    private CostRuleRepository costRuleRepository;

    //充电历史记录
    //TODO 不能用本地的MAP需要在ES中设置Map
    private Map<String, ChargingData> chargingProcessData = new HashMap<String, ChargingData>();

    //计算电费
    @Override
    public ChargingProcessVO calculateCost(ChargingProcessParam chargingProcessParam) {
        String orderNo = chargingProcessParam.getOrderNo();

        //通过订单编号获取上次充电记录
        ChargingData chargingData = chargingProcessData.get(orderNo);
        log.info("orderNo:{},chargingData:{}", orderNo, chargingData);

        if (chargingData == null) {
            log.debug("第一次充电 orderNo:{},chargingData:{}", orderNo, chargingData);
            //没有获取到数据,是第一次充电
            chargingData = new ChargingData();
            chargingData.setOrderNo(orderNo);
            chargingData.setUserId(chargingProcessParam.getUserId());
            chargingData.setStartTime(LocalDateTime.now());
            chargingProcessData.put(orderNo, chargingData);
        } else {
            log.debug("不是第一次充电 orderNo:{},chargingData:{}", orderNo, chargingData);
            BigDecimal totalCost = chargingData.getTotalCost();
        }

        //价格计算
        //不同的区域在不同的时间段,不同的枪类型 单度电的价格是不同的
        Integer stationId = chargingProcessParam.getStationId();
        //1.获取小时对应的价格
        List<CostRulePO> costRules = getCostRule(chargingProcessParam.getStationId());
        log.debug("获取计价规则:入参:{},出参:{}", stationId, costRules);

        //获取当前的时间,根据时间的不同,对应的价格是不一样的
        LocalDateTime now = LocalDateTime.now();
        //获取当前小时
        int hour = now.getHour();
        //循环当前计价规则
        for (CostRulePO costRule : costRules) {
            //判断当前同步的充电数据在哪个时间段
            if (hour >= costRule.getStartTime() && hour < costRule.getEndTime()) {
                //当前时段每度电的价格
                BigDecimal powerFee = costRule.getPowerFee();
                chargingData.setCurrentOneFee(powerFee);

                //判断是不是第一次充电
                if (chargingData.getLastchargingProcessParam() == null) {
                    //第一次上传充电记录(数据库中没有数据)
                    //获取充电度数
                    float chargingCapacity = chargingProcessParam.getChargingCapacity();
                    //把Float类型 转化为BigDecimal
                    BigDecimal chargingCapacityBigDecimal = new BigDecimal(Float.toString(chargingCapacity));
                    //计算价格
                    BigDecimal chargingCost = powerFee.multiply(chargingCapacityBigDecimal);
                    log.debug("当前时间:{},计费名称:{},每度电价格是:{}元,充电量:{},总花费:{}",hour,costRule.getName(),powerFee,chargingCapacity,chargingCost);
                    //记录本次充电费用
                    chargingData.setTotalCost(chargingCost);
                    //记录本次充电的记录到充电数据对象
                    chargingData.setLastchargingProcessParam(chargingProcessParam);
                } else {
                    //获取上次充电的记录
                    ChargingProcessParam prviousChargingProcessParam = chargingData.getLastchargingProcessParam();

                    //1:3  2:4  3:6  每次已经充了多少度 只需记录已经充了多少

                    //获取上次充电度数
                    float prviousChargingCapacity = prviousChargingProcessParam.getChargingCapacity();
                    //最新充电度数
                    float currentChargingCapacity = chargingProcessParam.getChargingCapacity();
                    //当前实际充电度数
                    float realChargingCapacity = currentChargingCapacity - prviousChargingCapacity;
                    BigDecimal  realChargingCapacityBigDecimal = new BigDecimal(Float.toString(realChargingCapacity));
                    //当前实际充电度数
                    BigDecimal currentChargingCost = powerFee.multiply(realChargingCapacityBigDecimal);
                    //取出上次的花费 + 本次花费 =目前花费
                    BigDecimal totalCost = chargingData.getTotalCost().add(currentChargingCost);
                    log.debug("当前时间:{},计费名称:{},每度电价格是:{}元,充电量:{},总花费:{}",hour,costRule.getName(),powerFee,realChargingCapacity,totalCost);
                    chargingData.setTotalCost(totalCost);
                    //保存本次充电的记录,方便下次充电时候读取
                    chargingData.setLastchargingProcessParam(chargingProcessParam);
                }
            }
        }
        //获取上次充电的度数
        //chargingProcessVO.setTotalCapacity(chargingData.getLastchargingProcessParam().getChargingCapacity());
        ChargingProcessVO chargingProcessVO = buildChargingProcessVO(chargingProcessParam, chargingData);
        return chargingProcessVO;
    }

    private ChargingDataTime getChargingDateTime(ChargingData chargingData) {
        Duration between = Duration.between(chargingData.getStartTime(),LocalDateTime.now());
        long hours = between.toHours();
        long minutes = between.toMinutes();
        long seconds = between.getSeconds();
        return new ChargingDataTime(hours,minutes,seconds);
    }

    @Override
    public ChargingProcessVO getOrderCost(String orderNo) {
        ChargingData chargingData = chargingProcessData.get(orderNo);
        //获取用户最后一次同步的数据(ChargingProcessParam 显示的是订单信息)
        ChargingProcessParam lastchargingProcessParam = chargingData.getLastchargingProcessParam();
        return buildChargingProcessVO(lastchargingProcessParam,chargingData);
    }

    //构建 ChargingProcessVO 返回对象
    /*
    * chargingProcessParam 充电过程中的参数
    * hargingData 表示的是充电参数
    * */
    private ChargingProcessVO buildChargingProcessVO(ChargingProcessParam chargingProcessParam,
                                                     ChargingData chargingData) {
        ChargingProcessVO chargingProcessVO = new ChargingProcessVO();
        BeanUtils.copyProperties(chargingProcessParam,chargingProcessVO);
        chargingProcessVO.setCurrentOneFee(chargingData.getCurrentOneFee());
        ChargingDataTime chargingDateTime = getChargingDateTime(chargingData);
        chargingProcessVO.setHours(chargingDateTime.getHours());
        chargingProcessVO.setMinutes(chargingDateTime.getMinutes());
        chargingProcessVO.setSeconds(chargingDateTime.getSeconds());
        chargingProcessVO.setTotalCost(chargingData.getTotalCost());
        return chargingProcessVO;
    }

    //缓存与数据库使用规则 cache-aside
    //通过充电站Id获取计价规则
    private List<CostRulePO> getCostRule(Integer stationId) {
        log.debug("通过充电站Id获取计价规则-入参 stationId={}", stationId);
        //通过充电站Id去 redis 中获取计价规则
        List<CostRulePO> cacheCostRulePO = cacheRepository.getCostRuleByStationId(stationId);
        log.debug("从Redis缓存中获取数据:{}", cacheCostRulePO);

        //判断 redis 中计价规则是否为空
        if (cacheCostRulePO == null) {
            //通过充电站Id去 Maraidb 中获取计价规则
            List<CostRulePO> costRulePos = costRuleRepository.getCostRuleByStationId(stationId);
            //判断 Maraidb计价规则是否为空
            if (!CollectionUtils.isEmpty(costRulePos)) {
                // Maraidb 计价规则不为空,保存计价规则到 redis 中
                cacheRepository.saveCostRule(stationId, costRulePos);
                //返回计价规则
                log.debug("从数据库获取数据:{}", costRulePos);
                return costRulePos;
            } else {
                log.warn("未找到计价规则,告警告警,停止充电");
            }
        }
        return cacheCostRulePO;
    }
}

